﻿using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Xml;
using SdmxMl.Common;
using SdmxMl.Helper;

namespace SdmxMl.GenericMetadata
{
    /// <summary>
    /// Reported attributes hold the values which are to be reported against the
    /// target specified in the metadata structure definition, and according to
    /// the metadata attributes specified for the target referenced in the TargetRef
    /// element. Each reported attribute may have Value sub-elements (one per language)
    /// if it takes a text or numeric value. The StartTime element is only used if
    /// the attribute being represented is of the Timespan type (as described in
    /// the corresponding TextFormat element in the metadata structure definition).
    /// In this case, the Value takes a duration. Only one such value is allowed in
    /// the ReportedAttribute in this case. The types of these values must conform to
    /// the limitations described in the metadata structure definition. Also - if
    /// permitted by the metadata structure definition - there may be one or more
    /// child ReportedAttribute elements. These must be arranged in the nesting hierarchy
    /// given in the metadat structure definition. The conceptID attribute provides the
    /// id of the concept given in the metadata structure definition to which the
    /// reported attribute corresponds.
    /// </summary>
    public class ReportedAttributeType : IAnnotationList, IReportedValue 
    {
        #region Properties

        private string _conceptId;
        public string ConceptID
        {
            get { return _conceptId; }
            set { _conceptId = value; }
        }

        protected MultiLang _value;
        public MultiLang Value
        {
            get { return _value; }
            set { _value = value; }
        }

        private DateTime _startTime;
        public DateTime StartTime
        {
            get { return _startTime; }
            set { _startTime = value; }
        }
        
        private AnnotationList annotationList;
        public AnnotationList AnnotationList
        {
            get { return annotationList; }
            set { annotationList = value; }
        }

        private ReportedAttributeTypeList _reportedAttributeList;
        public ReportedAttributeTypeList ReportedAttributeList
        {
            get { return _reportedAttributeList; }
        }

       #endregion //Properties

        #region Constructors

        public ReportedAttributeType()
        {
            Init();
        }

        private void Init()
        {
            _conceptId = "undefined";
            Value = new MultiLang();
            AnnotationList = new AnnotationList();
            _reportedAttributeList = new ReportedAttributeTypeList();
        }

        public ReportedAttributeType(XmlNode nodeArtefact, XmlNamespaceManager nsmgr)
        {
            Init();

            _conceptId = Xmlhelper.GetAtt(nodeArtefact, "conceptID");

            string stime = Xmlhelper.GetNodeText(nodeArtefact, "./genericmetadata:StartTime", nsmgr);
            if (stime.Length > 0)
                _startTime = Convert.ToDateTime(stime);

            // Reported Attributes 
            XmlNodeList nodeList = nodeArtefact.SelectNodes("./genericmetadata:ReportedAttribute", nsmgr);
            foreach (XmlNode nm in nodeList)
            {
                ReportedAttributeType r = new ReportedAttributeType(nm, nsmgr);
                _reportedAttributeList.Add(r);
            }

            MultiLangNodeReader mr = new MultiLangNodeReader();
            mr.Read(_value, nodeArtefact, "Value", "genericmetadata", nsmgr, false);

            nodeList = nodeArtefact.SelectNodes("./genericmetadata:Annotations/common:Annotation", nsmgr);
            foreach (XmlNode na in nodeList)
            {
                Annotation a = new Annotation(na, nsmgr);
                annotationList.Add(a);
            }
        }

        #endregion //Constructors

        #region Cloning

        public ReportedAttributeType Clone()
        {
            ReportedAttributeType clone = new ReportedAttributeType();
            clone.ConceptID = ConceptID;
            clone.AnnotationList = AnnotationList.Clone();
            clone.StartTime = StartTime;
            clone.Value = Value.Clone();
            foreach (ReportedAttributeType ra in ReportedAttributeList)
                clone.ReportedAttributeList.Add(ra.Clone());

            return clone;
        }
        #endregion

        #region XML serialize

        public void WriteXml(XmlTextWriter xw, string prefix, string tag)
        {
            xw.WriteStartElement(prefix, tag, null);

            // Required
            if (string.IsNullOrEmpty(ConceptID))
                ConceptID = "undefined";
            Xmlhelper.WriteAttributeIfExist(xw, "conceptID", _conceptId);

            //Value
            Value.WriteXml(xw, prefix, "Value");

            if (StartTime != DateTime.MinValue)
                xw.WriteElementString(prefix, "StartTime", null, StartTime.ToString("yyyy-MM-dd") + "T" + 
                    StartTime.ToString("HH:mm:ss-00:00"));

            // Reported Attributes 
            foreach (ReportedAttributeType r in _reportedAttributeList)
                r.WriteXml(xw, prefix, "ReportedAttribute");

            // Annotations
            AnnotationList.WriteXml(xw, prefix, null);

            xw.WriteEndElement();
        }

        #endregion //XML serialize

        #region String Representation

        public override string ToString()
        {
            return this.ConceptID;
        }

        #endregion // String Representation

        #region IReportedValue Members

        public MultiLang InternationalizedValue
        {
            get { return _value; }
        }

        string IReportedValue.SimpleValue
        {
            get
            {
                return null;
            }
            set
            {
                ;
            }
        }

        #endregion

        #region Filling state

        /// <summary>
        /// Attribute value or one of it's childs is filled
        /// </summary>
        /// <returns>true if one is filled at least</returns>
        public bool IsFilled()
        {
            bool oneFilled = false;
            if (Value.IsEmpty == false)
                oneFilled = true;
            else
            {
                foreach (ReportedAttributeType crt in this.ReportedAttributeList)
                {
                    if (crt.IsFilled())
                    {
                        oneFilled = true;
                        break;
                    }
                }
            }
            return oneFilled;
        }
        #endregion
    }

    /// <summary>
    /// Manages list of reported metadataset row attributes.
    /// </summary>
    public class ReportedAttributeTypeList : List<ReportedAttributeType>
    {
        #region Public helper Methods

        public bool DeleteRefcode(ReportedAttributeType crtTarget)
        {
            bool deleted = false;
            foreach (ReportedAttributeType crt in this)
            {
                if (crt == crtTarget)
                {
                    this.Remove(crt);
                    deleted = true;
                    break;
                }
                if (crt.ReportedAttributeList.DeleteRefcode(crtTarget) == true)
                {
                    deleted = true;
                    break;
                }
            }

            return deleted;
        }

        // Assume only one concept instance at max
        public ReportedAttributeType GetReportedAttributeOfConcept(string conceptId)
        {
            ReportedAttributeType refered = null;
            ReportedAttributeType target = null;
            foreach (ReportedAttributeType crt in this)
            {
                if (crt.ConceptID == conceptId)
                {
                    refered = crt;
                    break;
                }
                if ((target = crt.ReportedAttributeList.GetReportedAttributeOfConcept(conceptId)) != null)
                {
                    refered = target;
                    break;
                }
            }

            return refered;
        }
        // Assume only one concept instance at max
        public MultiLang GetReportedAttributeValueOfConcept(string conceptId)
        {
            MultiLang ml = new MultiLang();
            ReportedAttributeType refered = GetReportedAttributeOfConcept(conceptId);
            if (refered != null)
                ml = refered.InternationalizedValue.Clone();
            return ml;
        }

        public void GetReportedAttributeListForConcept(string conceptId, ReportedAttributeTypeList referedList)
        {
            if (referedList == null)
                return;

            foreach (ReportedAttributeType crt in this)
            {
                if (crt.ConceptID == conceptId)
                    referedList.Add(crt);
                crt.ReportedAttributeList.GetReportedAttributeListForConcept(conceptId, referedList);
            }
        }


        public bool IsConceptRefered(string conceptId)
        {
            return GetReportedAttributeOfConcept(conceptId) != null;
        }

        public ReportedAttributeTypeList GetCodexRefListOfCode(string code)
        {
            ReportedAttributeTypeList refered = null;
            ReportedAttributeTypeList refc = null;
            foreach (ReportedAttributeType crt in this)
            {
                if (crt.ConceptID == code)
                {
                    refered = this;
                    break;
                }
                if ((refc = crt.ReportedAttributeList.GetCodexRefListOfCode(code)) != null)
                {
                    refered = refc;
                    break;
                }
            }

            return refered;
        }
        #endregion

        #region Tree representation

        public void GetTreeHierarchyAsNodes(TreeNode parentNode)
        {
            foreach (ReportedAttributeType crt in this)
            {
                string name = crt.ConceptID;
                TreeNode nd = new TreeNode(name);
                nd.Tag = crt;
                parentNode.Nodes.Add(nd);
                crt.ReportedAttributeList.GetTreeHierarchyAsNodes(nd);
            }
        }
        #endregion

    }
}
